#ifndef DATA_POWER
#define DATA_POWER

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <assert.h>
#include <list>

#define CLUSTER_UNCLASSIFIED -2
#define CLUSTER_NOISE -1

using namespace std;

class PowerEntity {
    private:
        int clusterId;
    public:
        char date[11];
        char time[9];
        double globalActivePower;
        double globalReactivePower;
        double voltage;
        double globalIntensity;
        double subMetering1;
        double subMetering2;
        double subMetering3;

        PowerEntity(){
            clusterId = CLUSTER_UNCLASSIFIED;
        }

        void toString(){
            printf("d:%s t:%s p:%lf %lf %lf %lf %lf %lf %lf\n",
                   date, time, globalActivePower, globalReactivePower, voltage, globalIntensity,
                   subMetering1, subMetering2, subMetering3
            );
        }

        double euclidicDistance(PowerEntity *other){
            return sqrt(
                        (this->globalActivePower - other->globalActivePower) * (this->globalActivePower - other->globalActivePower)
                        + (this->globalReactivePower - other->globalReactivePower) * (this->globalReactivePower - other->globalReactivePower)
                        + (this->voltage - other->voltage) * (this->voltage - other->voltage)
                        + (this->globalIntensity - other->globalIntensity) * (this->globalIntensity - other->globalIntensity)
                        + (this->subMetering1 - other->subMetering1) * (this->subMetering1 - other->subMetering1)
                        + (this->subMetering2 - other->subMetering2) * (this->subMetering2 - other->subMetering2)
                        + (this->subMetering3 - other->subMetering3) * (this->subMetering3 - other->subMetering3)
            );
        }

        void setClusterId(int id){
            clusterId = id;
        }

        int getClusterId(){
            return clusterId;
        }
};

class PowerDatabase {
    unsigned int size;

    public:
    PowerEntity db[2100000];

    unsigned int getSize(){
        return size;
    }

    int load(char *filename, int recordLimit){
        PowerDatabase *power = this;
        int line=0;
        int scannedOnce=0;  // should be 9 as the number of entity properties
        int stopLoading=0;
        PowerEntity lineEntity;
        FILE *fp;
        if ((fp=fopen(filename, "r"))==NULL) {
            //printf("File opened\tERROR\n");
            return 0;
        } else {
            //printf("File opened\tOK\n");
        }
        // skip first line
        fscanf(fp, "%*[^\n]\n");
        // load entities
        while(!stopLoading){
            scannedOnce=fscanf(fp, "%10s%*c%8s%*c%lf%*c%lf%*c%lf%*c%lf%*c%lf%*c%lf%*c%lf",
               lineEntity.date, lineEntity.time, &lineEntity.globalActivePower, &lineEntity.globalReactivePower, &lineEntity.voltage, &lineEntity.globalIntensity,
               &lineEntity.subMetering1, &lineEntity.subMetering2, &lineEntity.subMetering3
            );
            if (line%50000==0){
                //printf("scannedOnce %d, line %d\n", scannedOnce, line);
                printf(".");
            }
            //entityPrint(lineEntity);

            if (scannedOnce!=9){
                if (scannedOnce==2){
                    // date and time present, parameters not present
                    // skip one line
                    fscanf(fp, "%*[^\n]\n");
                    continue;
                } else {
                    stopLoading = 1;
                    continue;
                }
            }
            // correctly scanned
            memcpy( &power->db[line], &lineEntity, sizeof(lineEntity) );
            if (line%50000==0){
                //entityPrint(power->db[line]);
            }
            ++line;
            if (recordLimit!=0){
                if (line>=recordLimit){
                    stopLoading = 1;
                    continue;
                }
            }
        }
        fclose(fp);
        power->size = line;
        printf("\nFile loaded lines %d\n", line);
        return 1;
    }

    double maxDistance(){
        PowerDatabase *power = this;
        unsigned int i;
        double r, max;
        max = 0.0;
        for(i=0; i<power->size; ++i){
            r = power->db[0].euclidicDistance(&power->db[i]);
            //printf("Dist %d %d = %lf\n", 0, i, r);
            if (r>max){
                max = r;
            }
        }
        return max;
    }

    /// DATASET INDEPENDENT BELOW

    list<PowerEntity*>* getEpsilonNeighborhood(PowerEntity * source, double epsilon){
        PowerDatabase *power = this;
        list<PowerEntity*>* neighborhood = new list<PowerEntity*>();
        unsigned int i;
        double r;
        assert(source);
        for(i=0; i<power->size; ++i){
            r = source->euclidicDistance(&power->db[i]);
            //printf("%lf\t",r);
            //printf("Dist %d %d = %lf\n", 0, i, r);
            if (r<=epsilon){
                if (&power->db[i]!=source){
                    neighborhood->push_back(&power->db[i]);
                }
            }
        }
        return neighborhood;
    }
};

#endif DATA_POWER
